#include "linux/of_irq.h"
#include "linux/irq.h"
#include "linux/irqdesc.h"
#include "linux/of.h"
#include "linux/errno.h"
#include "linux/printk.h"

#include "internals.h"

extern struct irq_chip no_irq_chip;

static irqreturn_t bad_chained_irq(int irq, void *dev_id)
{
	printk("Chained irq %d should not call an action\n", irq);
	return IRQ_NONE;
}

struct irqaction chained_action = {
	.handler = bad_chained_irq,
};


static inline void mask_ack_irq(struct irq_desc *desc)
{

}

static void irq_state_set_disabled(struct irq_desc *desc)
{
	irqd_set(&desc->irq_data, IRQD_IRQ_DISABLED);
}

struct irq_data *irq_get_irq_data(unsigned int irq)
{
	struct irq_desc *desc = irq_to_desc(irq);

	return desc ? &desc->irq_data : NULL;
}

uint32_t irqd_get_trigger_type(struct irq_data *d)
{
	return d->state_use_accessors & IRQD_TRIGGER_MASK;
}

unsigned int irq_get_trigger_type(unsigned int irq)
{
	struct irq_data *d = irq_get_irq_data(irq);
	return d ? irqd_get_trigger_type(d) : 0;
}

void *irq_get_chip_data(unsigned int irq)
{
	struct irq_data *d = irq_get_irq_data(irq);
	return d ? d->chip_data : NULL;
}

void *irq_data_get_irq_chip_data(struct irq_data *d)
{
	return d->chip_data;
}

void *irq_get_handler_data(unsigned int irq)
{
	struct irq_data *d = irq_get_irq_data(irq);
	return d ? d->handler_data : NULL;
}

void *irq_data_get_irq_handler_data(struct irq_data *d)
{
	return d->handler_data;
}


int irq_set_chip(unsigned int irq, struct irq_chip *chip)
{
	unsigned long flags;
	struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);

	if (!desc)
		return -EINVAL;

	if (!chip)
		chip = &no_irq_chip;

	desc->irq_data.chip = chip;
	irq_put_desc_unlock(desc, flags);
	
	return 0;
}

int irq_set_irq_type(unsigned int irq, unsigned int type)
{
	unsigned long flags;
	struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
	int ret = 0;

	if (!desc)
		return -EINVAL;

	ret = __irq_set_trigger(desc, type, flags);
	irq_put_desc_busunlock(desc, flags);
	return ret;
}

int irq_set_handler_data(unsigned int irq, void *data)
{
	return 0;
}

void __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,char *name)
{
	unsigned long flags;
	struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);

	if (!desc)
		return;

	if (!handle)
	{
		handle = handle_bad_irq;
	}
	else
	{
		struct irq_data *irq_data = &desc->irq_data;
		if (WARN_ON(!irq_data || irq_data->chip == &no_irq_chip))
			goto out;
	}

	/* Uninstall? */
	if (handle == handle_bad_irq)
	{
		if (desc->irq_data.chip != &no_irq_chip)
			mask_ack_irq(desc);
		irq_state_set_disabled(desc);
		desc->depth = 1;
	}
	desc->handle_irq = handle;
	desc->name = name;

	if (handle != handle_bad_irq && is_chained)
	{
		//irq_settings_set_noprobe(desc);
		//irq_settings_set_norequest(desc);
		//irq_settings_set_nothread(desc);
		//irq_startup(desc, true);
	}
out:
	irq_put_desc_busunlock(desc, flags);
}

void irq_set_chip_and_handler_name(unsigned int irq, struct irq_chip *chip, irq_flow_handler_t handle,char *name)
{
	irq_set_chip(irq, chip);
	__irq_set_handler(irq, handle, 0, name);
}

void irq_set_chip_and_handler(unsigned int irq, struct irq_chip *chip, irq_flow_handler_t handle)
{
	irq_set_chip_and_handler_name(irq, chip, handle, NULL);
}

int irq_set_chip_data(unsigned int irq, void *data)
{
	unsigned long flags;
	struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);

	if (!desc)
		return -EINVAL;
	desc->irq_data.chip_data = data;
	irq_put_desc_unlock(desc, flags);
	return 0;
}

void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set)
{

}

static void __irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle, int is_chained,char *name)
{
	desc->handle_irq = handle;
	desc->name = name;

	if (handle != handle_bad_irq && is_chained)
	{
		//irq_settings_set_noprobe(desc);
		//irq_settings_set_norequest(desc);
		//irq_settings_set_nothread(desc);
		//irq_startup(desc, true);
		desc->action = &chained_action;
	}
}

void irq_set_chained_handler_and_data(unsigned int irq, irq_flow_handler_t handle, void *data)
{
	unsigned long flags;
	struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);

	if (!desc)
		return;

	desc->irq_common_data.handler_data = data;
	__irq_do_set_handler(desc, handle, 1, NULL);

	irq_put_desc_busunlock(desc, flags);
}

void handle_level_irq(unsigned int irq, struct irq_desc *desc)
{
	//raw_spin_lock(&desc->lock);
	//mask_ack_irq(desc);

	desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);

	/*
	 * If its disabled or no action available
	 * keep it masked and get out of here
	 */
	if (!desc->action || irqd_irq_disabled(&desc->irq_data))
	{
		desc->istate |= IRQS_PENDING;
		goto out_unlock;
	}

	//kstat_incr_irqs_this_cpu(desc);
	handle_irq_event(desc);

	//cond_unmask_irq(desc);

out_unlock:
	//raw_spin_unlock(&desc->lock);
	return;
}

void handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc)
{
	handle_irq_event(desc);
}

void irq_enable(struct irq_desc *desc)
{
	//irq_state_clr_disabled(desc);
	if (desc->irq_data.chip->irq_enable)
		desc->irq_data.chip->irq_enable(&desc->irq_data);
	else
		desc->irq_data.chip->irq_unmask(&desc->irq_data);
	//irq_state_clr_masked(desc);
}


int irq_startup(struct irq_desc *desc, int resend)
{
	int ret = 0;

	irq_enable(desc);
	
	return ret;
}
